package org.sardine.beans;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class XSDValidator {
	
	private static final String PROTOCOL_FILE = "file:";
	private static final String XML_RESOURCE = "resources/test.xml";
	private static final String XSD_RESOURCE = "resources/test.xsd";
	private static final String SCHEMA_LANGUAGE_NAME = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
	private static final String SCHEMA_LANGUAGE_VALUE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
	
	private final Logger log = Logger.getLogger(XSDValidator.class);
	
	public static void main(String[] args) throws Exception{
		
		XSDValidator v = new XSDValidator();
		//v.testInputStream();
		v.validate();
		//v.validateWithoutParser();
		//v.validateWithoutParser();
		
	}
	
	private void testInputStream() throws IOException{
		
		InputStreamReader r = null;
		try {
			r = new InputStreamReader(new FileInputStream(XML_RESOURCE));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		BufferedReader reader = new BufferedReader(r);
		
		String line;
		while((line = reader.readLine())!= null){
			log.info(line);
		} 
		
		
	}
	
	/**
	 * 
	 * @疑问
	 * 一，当parse方法的参数是File和URI时，是通过构造方法InputSource(String systemid)创建InputSource，并传给XML解析器的实例。程序正常，可以通过XSD验证XML文档。
	 * 二，当parse方法的参数是InputSource时，且这个InputSource是通过构造方法InputSource(String systemid)创建，同上。程序正常，可以通过XSD验证XML文档。
	 * 三，当parse方法的参数是InputStream时，通过构造方法InputSource(InputStream is)创建InputSource，并传给XML解析器的实例。就会出现找不到XSD的错误。
	 * 
	 * 三这种情况是因为以InputStream创建的InputSource缺失systemId这个属性，调用setSystemId()即可。为何要多此一举？
	 * 
	 * @throws Exception
	 */
	private void validate() throws Exception{
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		factory.setValidating(true);
		factory.setAttribute(SCHEMA_LANGUAGE_NAME, SCHEMA_LANGUAGE_VALUE);
		DocumentBuilder builder = factory.newDocumentBuilder();
		builder.setErrorHandler(new SimpleErrorHandler());
		
		// ************ 以下代码正常，能识别到XSD
		
		// 从源码层看，两者parse一样
		//Document doc = builder.parse(new File(XML_RESOURCE));
		//String rs = (new File(XML_RESOURCE)).toURI().toASCIIString();
		//log.info(rs);
		//Document doc = builder.parse(new InputSource(rs));
		
		// 从源码层看，两者parse一样
		//Document doc = builder.parse(XML_RESOURCE);
		//InputSource is = new InputSource(XML_RESOURCE);
		//log.info(is.getSystemId());		// resources/test.xml
		//Document doc = builder.parse(is);
		
		// ************ 以下会找不到XSD
		
		// 从源码层看，两者parse一样
		//Document doc = builder.parse(new FileInputStream(XML_RESOURCE));
		//InputSource is = new InputSource(new FileInputStream(XML_RESOURCE));
		//log.info(is.getSystemId());			// 空
		//Document doc = builder.parse(is);
		
		//URL url = new URL(PROTOCOL_FILE+XML_RESOURCE);
		//URLConnection connection = url.openConnection();
		//InputSource is = new InputSource(new FileInputStream(XML_RESOURCE));
		//log.info(is.getSystemId());				// 空
		//Document doc = builder.parse(is);
		
		
		// ************ 以下设置了 systemId后，代码又正常了，能识别到XSD
		URL url = new URL(PROTOCOL_FILE+XML_RESOURCE);
		URLConnection connection = url.openConnection();
		InputSource is = new InputSource(new FileInputStream(XML_RESOURCE));
		is.setSystemId(XML_RESOURCE);
		log.info(is.getSystemId());				// resources/test.xml
		Document doc = builder.parse(is);
		
		/*InputSource is = new InputSource();
		is.setSystemId(XML_RESOURCE);
		log.info(is.getSystemId());
		Document doc = builder.parse(is);*/
		
		
		Element elm = doc.getDocumentElement();
		log.info(elm.getTagName());
		
	}
	
	private void validateWithParser() throws Exception{
		
		SchemaFactory sf = SchemaFactory.newInstance(SCHEMA_LANGUAGE_VALUE);
		Schema schema = sf.newSchema(new URL(PROTOCOL_FILE + XSD_RESOURCE));
		
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setSchema(schema);
		DocumentBuilder builder = factory.newDocumentBuilder();
		builder.setErrorHandler(new SimpleErrorHandler());
		Document doc = builder.parse(new File(XML_RESOURCE));
		
		
	}
	
	private void validateWithoutParser() throws Exception{
		
		SchemaFactory sf = SchemaFactory.newInstance(SCHEMA_LANGUAGE_VALUE);
		Schema schema = sf.newSchema(new URL(PROTOCOL_FILE + XSD_RESOURCE));
		
		Validator v = schema.newValidator();
		v.setErrorHandler(new SimpleErrorHandler());
		v.validate(new StreamSource(new File(XML_RESOURCE)));
		
	}
	
	public class SimpleErrorHandler implements ErrorHandler{

		public void error(SAXParseException e) throws SAXException {
			throw e;
			
		}

		public void fatalError(SAXParseException e) throws SAXException {
			throw e;
			
		}

		public void warning(SAXParseException e) throws SAXException {
			throw e;
			
		}
		
	}

}
